Similaridade, derivação e plágio em software: comparação estrutural e binária

Introdução

Quando dois softwares parecem cumprir a mesma função, surge a pergunta: até que ponto eles são semelhantes — e por quê? Um exame técnico não se limita a “percentuais de coincidência”. Ele reconstrói o caminho das evidências: como os materiais foram obtidos e preservados; em que nível do programa a semelhança aparece; quão estável ela permanece diante de perturbações como refatoração, minificação ou recompilação.

Aquisição e preservação

O exame começa com a aquisição controlada dos artefatos: repositórios (Git, Mercurial, SVN), árvores de diretórios com código-fonte, bytecodes (JVM/.NET), binários (PE/ELF/Mach-O) e arquivos de configuração de build. Cada item é catalogado com hash criptográfico (por exemplo, SHA-256), tamanho, carimbos de data e hora e origem.
A seguir, procede-se a uma normalização mínima: registros de ambiente, timezone, versões de ferramenta e, quando cabível, a fixação de dependências (como package-lock.json ou poetry.lock). Essa disciplina garante reprodutibilidade — premissa central de qualquer inferência técnica.

Onde procurar similaridade

A comparação avança por camadas. Em geral, parte-se do texto e progride rumo a representações mais estruturais ou de menor nível, conforme a disponibilidade dos materiais.

Texto e tokens.

Com o código-fonte em mãos, a tokenização por linguagem (removendo comentários e espaços) permite detectar cópias literais ou com alterações superficiais, como renomeações. Métricas de distância entre sequências (por exemplo, shingles e distâncias de edição) constroem um panorama inicial: trechos que se repetem, extensão da coincidência e distribuição pelo projeto.

Estrutura sintática (AST).

Ao converter o código em árvores sintáticas (AST), comparamos a organização estrutural da lógica. Coincidências aqui são mais robustas do que no nível textual, pois sobrevivem a mudanças cosméticas. Métodos comuns incluem hashing de subárvores e cálculo de distâncias entre árvores.

Fluxo de controle e de dados.

Para investigar semelhanças funcionais, analisam-se grafos de fluxo de controle (CFG) e dependência (PDG). A pergunta muda de forma: não “as mesmas palavras”, mas “os mesmos caminhos de execução ou relações entre instruções”. É mais custoso, porém captura semelhanças que resistem a refatorações extensas.

Bytecode e IR.

Em plataformas como JVM e .NET, ou com IRs (como LLVM), sequências de instruções, perfis de opcode e padrões de inlining revelam parentescos mesmo sem acesso ao fonte.

Binários.

Na ausência do código-fonte, a comparação recorre à engenharia reversa: seções, tabelas de símbolos e imports/exports, strings, assinaturas de compilador/packer, além de fuzzy hashing e diffing função a função. Ofuscações complexas e remoção de símbolos impõem limites, mas ainda é possível delinear padrões de semelhança.

Um percurso de análise, do grosso ao fino

O procedimento típico combina amplitude e profundidade:

  • Triagem informada.

Identifica-se e exclui-se conteúdo de terceiros (frameworks, bibliotecas), com base em listas de permissão e arquivos de lock. Essa etapa evita inflar artificialmente os índices de coincidência.

  • Normalização.

Formatação estável, renomeação canônica de identificadores e ordenação previsível de imports reduzem ruído. Em código web, desminificação e análise de “source maps” ajudam a desfazer empacotamentos.

  • Comparação multinível

Passa-se do token/AST ao fluxo e, se necessário, a bytecode/binário. Em cada nível, registram-se trechos, offsets, funções, hashes e versões de ferramenta.

  • Agregação.

Os resultados são sintetizados por arquivo, módulo e projeto, ponderando tamanho e raridade dos padrões. Coincidências triviais (boilerplate) recebem peso menor.

  • Robustez.

O estudo é repetido após perturbações controladas: renomeações aleatórias, recompilações com flags distintas, minificação ou obfuscação. Conclusões sólidas devem persistir nessas condições.

Como ler as métricas

Em relatórios técnicos, números aparecem em família, não isolados. Alguns eixos comuns:

  • Cobertura: fração do material impactada pela coincidência (em tokens, nós de AST ou funções).
  • Granularidade: extensão média dos blocos coincidentes; blocos longos e raros pesam mais do que fragmentos curtos e comuns.
  • Densidade: frequência de matches por mil linhas ou por função, útil para mapear “zonas de convergência”.
  • Consistência entre níveis: coincidências que se repetem em dois ou mais níveis (por exemplo, AST e fluxo) tendem a ser mais informativas.
  • Limiar técnico: definido por benchmarks de controle e explicitado com parâmetros e versões. Não há “percentual universal”.

Em suma, porcentagens sozinhas pouco dizem. A interpretação vem do conjunto de evidências, ponderando distribuição, raridade e estabilidade dos achados.

Obfuscação, minificação e refatoração

Técnicas de ofuscação e empacotamento buscam confundir análises superficiais. A resposta é ajustar o nível de observação:

  • Minificação e bundling: reverter quando possível; focar em estruturas e não apenas em layout.
  • Obfuscação: reduzir efeitos de renomeação, priorizar perfis de execução e de opcode.
  • Refatorações: confiar mais em AST, CFG e PDG do que em coincidências textuais.
  • Portabilidade entre linguagens: ao migrar (por exemplo, Java→Kotlin), comparar a organização lógica e não apenas os símbolos.

Limitações técnicas

Alguns fatores exigem cautela:

  • Conteúdos de terceiros e código gerado (ORMs, builders, IA) podem amplificar coincidências por construção.
  • Ofuscação avançada e virtualização de código degradam a sensibilidade.
  • Diferenças de compilador, otimizador e ferramentas de parsing introduzem vieses.
  • Mesmo alta similaridade não prova causalidade. O exame técnico oferece evidências, não a narrativa histórica de como os códigos surgiram.

Relato e reprodutibilidade

Um bom relatório descreve o que foi comparado, como foi comparado e o que foi encontrado, de modo que outro perito consiga refazer o caminho:

  • Escopo (conjuntos, versões, hashes).
  • Metodologia (níveis, ferramentas, parâmetros, corpus de controle).
  • Resultados (panorama geral, casos representativos, mapas de funções/trechos).
  • Testes de robustez e sensibilidade.
  • Discussão técnica (raridade, distribuição, plausibilidade de convergência).
  • Conclusões técnicas, com limites claramente declarados.
  • Anexos (listas, trechos, logs, scripts).

A clareza metodológica não é um adorno; é o próprio lastro da conclusão.

Referências técnicas

  • Diretrizes de aquisição e preservação de evidências digitais e de cadeia de custódia.
  • Literatura de code clone detection (tipos I–IV; AST, PDG e diffs binários).
  • Estudos sobre fuzzy hashing e comparação de binários.
  • Boas práticas de reprodutibilidade computacional (fixação de ambiente, versões e sementes).

Em cada exame, as referências específicas — com versões de ferramentas e parâmetros — devem ser listadas com data e identificadores persistentes, quando houver.

A avaliação de similaridade e possível derivação em software é mais convincente quando progride de forma metódica, documenta todas as etapas e testa a resistência de seus achados. Trata-se de construir um quadro técnico coerente, onde diferentes camadas de análise convergem — ou não — para a mesma história.

Image:iStock.com/Yurich84

Veja também